package com.s7175.kor.data;

import java.io.File;
import java.util.LinkedList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.s7175.kor.model.ArchitectureView;
import com.s7175.kor.model.Attribute;
import com.s7175.kor.model.Component;
import com.s7175.kor.model.Parameter;
import com.s7175.kor.model.Server;

public class DatabaseParser {

	public static final String ENTRY_ARCHITECTURE = "architecture";
	public static final String ENTRY_COMPONENT = "component";
	public static final String ENTRY_ATTRIBUTE = "attribute";
	public static final String ENTRY_SERVER = "server";
	public static final String ENTRY_CONFIGURATION = "configurationitem";
	public static final String ENTRY_PARAMETER = "parameter";

	public static final String ENTRY_ARCHITECTURE_NAME = "name";
	public static final String ENTRY_COMPONENT_NAME = "name";
	public static final String ENTRY_ATTRIBUTE_NAME = "name";

	public static final String ENTRY_CONFIGURATION_NAME = "name";
	public static final String ENTRY_CONFIGURATION_CORES = "cores";
	public static final String ENTRY_CONFIGURATION_USERS = "users";
	public static final String ENTRY_CONFIGURATION_DEBUG = "debug";

	public static final String ENTRY_PARAMETER_NAME = "name";

	public static final String ENTRY_SERVER_NAME = "name";
	public static final String ENTRY_SERVER_ID = "id";
	public static final String ENTRY_SERVER_DOMAIN = "domain";

	public static final boolean saveDatabank(String sFile,
			DatabasePlaceholder oHolder) {

		DocumentBuilderFactory docFactory = DocumentBuilderFactory
				.newInstance();
		try {
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			Document doc = docBuilder.newDocument();

			for (ArchitectureView arch : oHolder.getArchList()) {

				Element rootElement = doc.createElement(ENTRY_ARCHITECTURE);
				rootElement.setAttribute(ENTRY_ARCHITECTURE_NAME,
						arch.getName());
				doc.appendChild(rootElement);

				for (Component comp : arch.getComponents()) {
					walkOverComponent(doc, comp, rootElement);
				}

				// write the content into xml file
				TransformerFactory transformerFactory = TransformerFactory
						.newInstance();
				Transformer transformer = transformerFactory.newTransformer();
				DOMSource source = new DOMSource(doc);
				StreamResult result = new StreamResult(new File(
						DB40Data.DATABASE_SOURCE));

				// Output to console for testing
				// StreamResult result = new StreamResult(System.out);

				transformer.transform(source, result);

				System.out.println("File saved!");

			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			// yeah bad practice... studies!
			e.printStackTrace();
		}

		return true;
	}

	private static final boolean walkOverComponent(Document doc,
			Component compo, Element parent) {

		Element compoEl = doc.createElement(ENTRY_COMPONENT);
		compoEl.setAttribute(ENTRY_COMPONENT_NAME, compo.getName());

		for (Attribute attr : compo.getAttributes()) {
			Element attrElement = doc.createElement(ENTRY_ATTRIBUTE);
			attrElement.setAttribute(ENTRY_ATTRIBUTE_NAME, attr.getName());
			attrElement.setTextContent(attr.getValue());
			compoEl.appendChild(attrElement);
		}

		// append servers too
		for (Server srv : compo.getServers()) {
			Element srvElement = doc.createElement(ENTRY_SERVER);
			srvElement.setAttribute(ENTRY_SERVER_NAME, srv.getName());
			srvElement.setAttribute(ENTRY_SERVER_DOMAIN, srv.getDomain());
			srvElement.setAttribute(ENTRY_SERVER_ID,
					String.valueOf(srv.getId()));

			com.s7175.kor.model.Configuration conf = srv.getConfItem();

			Element confElement = doc.createElement(ENTRY_CONFIGURATION);
			confElement.setAttribute(ENTRY_CONFIGURATION_NAME, conf.getName());

			Element coresElement = doc.createElement(ENTRY_CONFIGURATION_CORES);
			coresElement.setTextContent(String.valueOf(conf.getCores()));
			confElement.appendChild(coresElement);

			Element usersElement = doc.createElement(ENTRY_CONFIGURATION_USERS);
			usersElement.setTextContent(String.valueOf(conf.getUsers()));
			confElement.appendChild(usersElement);

			Element debugElement = doc.createElement(ENTRY_CONFIGURATION_DEBUG);
			debugElement.setTextContent(String.valueOf(conf.getDebug()));
			confElement.appendChild(debugElement);

			for (Parameter param : conf.getParameters()) {
				Element paramElement = doc.createElement(ENTRY_PARAMETER);
				paramElement.setTextContent(param.getValue());
				paramElement
						.setAttribute(ENTRY_PARAMETER_NAME, param.getName());
				confElement.appendChild(paramElement);
			}

			// append in reverse
			srvElement.appendChild(confElement);
			compoEl.appendChild(srvElement);
		}

		for (Component childCompo : compo.getComponents()) {
			walkOverComponent(doc, childCompo, compoEl);
		}

		parent.appendChild(compoEl);

		return true;
	}

	public static final DatabasePlaceholder parseDatabank(String sFile) {

		DatabasePlaceholder oHolder = null;
		final LinkedList<com.s7175.kor.model.ArchitectureView> oArchList = new LinkedList<com.s7175.kor.model.ArchitectureView>();
		final LinkedList<com.s7175.kor.model.Server> oServerList = new LinkedList<com.s7175.kor.model.Server>();
		final LinkedList<com.s7175.kor.model.Attribute> oAttributeList = new LinkedList<com.s7175.kor.model.Attribute>();
		final LinkedList<com.s7175.kor.model.Component> oComponentList = new LinkedList<com.s7175.kor.model.Component>();
		final LinkedList<com.s7175.kor.model.Configuration> oConfigurationList = new LinkedList<com.s7175.kor.model.Configuration>();
		final LinkedList<com.s7175.kor.model.Parameter> oParameterList = new LinkedList<com.s7175.kor.model.Parameter>();

		try {

			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();

			DefaultHandler handler = new DefaultHandler() {

				boolean arch = false;
				boolean compo = false;
				boolean attr = false;
				boolean server = false;

				boolean config = false;
				boolean configCore = false;
				boolean configUsers = false;
				boolean configDebug = false;
				boolean param = false;

				Component oComponent = null;
				ArchitectureView oArch = null;
				Attribute oAttr = null;
				Server oServer = null;
				Parameter oParam = null;
				com.s7175.kor.model.Configuration oConf = null;

				public void startElement(String uri, String localName,
						String qName, Attributes attributes)
						throws SAXException {

					// //System.out.println("Start Element :" + qName);

					if (qName.equalsIgnoreCase(ENTRY_ARCHITECTURE)) {
						arch = true;

						if (attributes != null) {
							for (int i = 0; i < attributes.getLength(); ++i) {
								// add new architecture
								if (attributes.getQName(i).equalsIgnoreCase(
										ENTRY_ARCHITECTURE_NAME)) {
									oArch = new ArchitectureView(
											attributes.getValue(i));
									oArchList.add(oArch);
									// //System.out.println("Added arch... "
									// + attributes.getValue(i));
								}
							}
						}

					}

					if (qName.equalsIgnoreCase(ENTRY_SERVER)) {
						server = true;

						if (attributes != null) {
							String sName = null;
							String sDomain = null;
							Integer iID = null;
							for (int i = 0; i < attributes.getLength(); ++i) {
								// add new architecture
								if (attributes.getQName(i).equalsIgnoreCase(
										ENTRY_SERVER_ID)) {
									try {
										iID = Integer.valueOf(attributes
												.getValue(i));
									} catch (Exception e) {
										System.out.println("error "
												+ e.getMessage());
									}
								} else if (attributes.getQName(i)
										.equalsIgnoreCase(ENTRY_SERVER_DOMAIN)) {
									sDomain = attributes.getValue(i);
								} else if (attributes.getQName(i)
										.equalsIgnoreCase(ENTRY_SERVER_NAME)) {
									sName = attributes.getValue(i);
								}

							}
							// //System.out.println(sName);
							// //System.out.println(sDomain);
							// //System.out.println(iID.toString());
							if (sName != null && sDomain != null && iID != null) {
								oServer = new Server(sName, sDomain, iID);
								oServerList.add(oServer);
								oServer.setWorksIn(oComponent);
								oComponent.addServer(oServer);
							}
						}

					}

					if (qName.equalsIgnoreCase(ENTRY_COMPONENT)) {

						for (int i = 0; i < attributes.getLength(); ++i) {
							// add new architecture
							if (attributes.getQName(i).equalsIgnoreCase(
									ENTRY_COMPONENT_NAME)) {

								if (compo) {
									// System.out
									// .println("adding new compo to parent : "
									// + oComponent.getName());
									// a child for current component
									Component oChild = new Component(
											attributes.getValue(i));
									oComponent.addChild(oChild);
									oComponentList.add(oChild);
									oChild.setParent(oComponent);
									oComponent = oChild;
								} else {
									// new one

									oComponent = new Component(
											attributes.getValue(i));
									// System.out.println("adding new compo "
									// + oComponent.getName());
									oComponentList.add(oComponent);
									oArch.addComponent(oComponent);
								}

								// //System.out.println("Component added... "
								// + attributes.getValue(i));
							}
						}

						compo = true;
					}

					if (qName.equalsIgnoreCase(ENTRY_ATTRIBUTE)) {
						for (int i = 0; i < attributes.getLength(); ++i) {
							// add new architecture
							if (attributes.getQName(i).equalsIgnoreCase(
									ENTRY_ATTRIBUTE_NAME)) {
								oAttr = new Attribute(attributes.getValue(i));
								oAttr.setComponent(oComponent);
								oAttributeList.add(oAttr);
							}
						}
						attr = true;
					}

					if (qName.equalsIgnoreCase(ENTRY_PARAMETER)) {
						for (int i = 0; i < attributes.getLength(); ++i) {
							if (attributes.getQName(i).equalsIgnoreCase(
									ENTRY_PARAMETER_NAME)) {
								// System.out.println(attributes.getValue(i));
								oParam = new Parameter(attributes.getValue(i));
								oParam.setBelongTo(oConf);
								oParameterList.add(oParam);
							}
						}
						param = true;
					}

					if (qName.equalsIgnoreCase(ENTRY_CONFIGURATION)) {
						for (int i = 0; i < attributes.getLength(); ++i) {
							if (attributes.getQName(i).equalsIgnoreCase(
									ENTRY_CONFIGURATION_NAME)) {
								oConf = new com.s7175.kor.model.Configuration(
										attributes.getValue(i));
								oConf.setWorksOn(oServer);
								oConfigurationList.add(oConf);
								oServer.setConfItem(oConf);
							}
						}
						config = true;
					}

					if (qName.equalsIgnoreCase(ENTRY_CONFIGURATION_CORES)) {
						if (oConf != null) {
							configCore = true;
						}
					}

					if (qName.equalsIgnoreCase(ENTRY_CONFIGURATION_USERS)) {
						if (oConf != null) {
							configUsers = true;
						}
					}

					if (qName.equalsIgnoreCase(ENTRY_CONFIGURATION_DEBUG)) {
						if (oConf != null) {
							configDebug = true;
						}
					}
				}

				public void endElement(String uri, String localName,
						String qName) throws SAXException {

					if (qName.equalsIgnoreCase(ENTRY_COMPONENT)) {

						if (compo) {
							// finishing component?

							// System.out.println("finish compo element...");

							if (oComponent != null
									&& oComponent.getParent() != null) {
								// System.out.println("parent is not null!");
								oComponent = oComponent.getParent();
							} else {
								oComponent = null;
								// System.out.println("parent is null");
								compo = false;
							}
						}
					}

					// System.out.println("End Element :" + qName);

				}

				public void characters(char ch[], int start, int length)
						throws SAXException {

					if (arch) {
						// //System.out.println("arch Name : "
						// + new String(ch, start, length));
						arch = false;
					}

					if (compo) {
						// //System.out.println("compo Name : "
						// + new String(ch, start, length));
					}

					if (server) {
						// //System.out.println("server data : "
						// + new String(ch, start, length));
						server = false;
					}

					if (config) {
						// System.out.println("config data : "
						// + new String(ch, start, length));
						config = false;
					}

					if (configCore) {
						oConf.setCores(Integer.valueOf((new String(ch, start,
								length))));
						configCore = false;
					}

					if (configUsers) {
						oConf.setUsers(Integer.valueOf((new String(ch, start,
								length))));
						configUsers = false;
					}

					if (configDebug) {
						oConf.setDebug(Boolean.valueOf((new String(ch, start,
								length))));
						configDebug = false;
					}

					if (param) {
						// System.out.println("param val : "
						// + new String(ch, start, length));
						oParam.setValue(new String(ch, start, length));
						oConf.addParameter(oParam);
						param = false;
					}

					if (attr) {
						// //System.out.println("attr Name : "
						// + new String(ch, start, length));
						oAttr.setValue(new String(ch, start, length));
						oComponent.addAttribute(oAttr);
						attr = false;
					}

				}

			};

			saxParser.parse(sFile, handler);
		} catch (Exception e) {
			e.printStackTrace();
		}

		oHolder = new DatabasePlaceholder();
		oHolder.setArchList(oArchList);
		oHolder.setServerList(oServerList);
		oHolder.setParameterList(oParameterList);
		oHolder.setConfigurationList(oConfigurationList);
		oHolder.setComponentList(oComponentList);
		oHolder.setAttributeList(oAttributeList);
		return oHolder;

	}
}
